using System;
using Impact.Model.Objects;
using Impact.Model.Service;
using StructureMap;
using System.Net;
using System.IO;
using System.Collections.Generic;
using Impact.Infrastructure.Exceptions;
using Impact.Infrastructure.Logging;

namespace Impact.Controllers
{
    /// <summary>
    /// Controller for location finding logic
    /// </summary>
    public class LocationController
    {

        /// <summary>
        /// for exceptions thrown by the LocationController
        /// </summary>
        public class LocationException : System.ApplicationException
        {
            /// <summary>
            /// default constructor
            /// </summary>
            public LocationException( string message ) : base( message ) { }
        }

        /// <summary>
        /// return the list of profile that are closest to the given zipcode
        /// </summary>
        /// <param name="zipcode"></param>
        /// <param name="count">number of results to return</param>
        /// <returns></returns>
        public static List<Profile> FindClosestProfiles( string zipcode, int count )
        {
            Check.Require( zipcode, "Zipcode is required" );
            IProfileService service = ObjectFactory.GetInstance<IProfileService>();
            ProfileCollection profiles = service.FetchProfiles( true );

            float lat1, long1;
            string location;

            if ( GetLocationFromZip( zipcode, out lat1, out long1, out location ) )
            {

                foreach ( Profile profile in profiles )
                {
                    float lat2 = profile.Latitude;
                    float long2 = profile.Longitude;

                    float longAdj = ( long1 - long2 ) * (float)Math.Cos( deg2rad( ( lat1 + lat2 ) / 2 ) );
                    profile.Distance = pythag( longAdj, lat1 - lat2 ) * 69;
                }

                profiles.GetList().Sort( delegate( Profile a, Profile b )
                {
                    return a.Distance.CompareTo( b.Distance );
                } );
            }
            else
            {
                throw new LocationException( "Could not find zipcode.  Please try another." );
            }

            if ( profiles.Count > count )
                return profiles.GetList().GetRange( 0, count );
            else
                return profiles.GetList();
        }

        /// <summary>
        /// just your average pythagorean theorem:  a^2 + b^2 = c^2, therefore c = sqrt of (a^2 + b^2)
        /// </summary>
        private static float pythag( float a, float b )
        {
            return (float)Math.Sqrt( Math.Pow( (double)a, 2.0 ) + Math.Pow( (double)b, 2.0 ) );
        }

        /// <summary>
        /// converts degrees to radians
        /// </summary>
        private static double deg2rad( float degrees )
        {
            return (double)degrees * 0.0174532925;
        }

        /// <summary>
        /// given a zipcode, return the pertinant location information
        /// </summary>
        /// <returns>true if found, false if not</returns>
        public static bool GetLocationFromZip( string zipcode, out float latitude, out float longitude, out string location )
        {
            try
            {
                string url = string.Format( "http://geocoder.us/service/csv/geocode?zip={0}", zipcode );
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create( url );
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                StreamReader reader = new StreamReader( response.GetResponseStream() );
                string output = reader.ReadToEnd();
                string[] parts = output.Split( ',' );

                latitude = float.Parse( parts[0] );
                longitude = float.Parse( parts[1] );
                location = parts[2] + ", " + parts[3];

                return true;
            }
            catch ( Exception ex )
            {
                Logger.Write( ex );
                
                latitude = 0.0F;
                longitude = 0.0F;
                location = string.Empty;
                return false;
            }
        }


    }

}
